﻿///////////////////////////////////////////////////////////////////////////////
//	Copyright 2013 JASDev International
//
//	Licensed under the Apache License, Version 2.0 (the "License");
//	you may not use this file except in compliance with the License.
//	You may obtain a copy of the License at
//
//		http://www.apache.org/licenses/LICENSE-2.0
//
//	Unless required by applicable law or agreed to in writing, software
//	distributed under the License is distributed on an "AS IS" BASIS,
//	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//	See the License for the specific language governing permissions and
//	limitations under the License.
///////////////////////////////////////////////////////////////////////////////

using System;
using System.Net;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.IO;
using Microsoft.SPOT.Net.NetworkInformation;

using GHI.Premium.IO;
using GHI.Premium.Net;

using JDI.Common;
using JDI.Common.Extensions;
using JDI.Common.Logger;
using JDI.Common.Utils;
using JDI.WebSocket.Client;

namespace JDI.WebSocket.MFConsoleApp
{
	public class Program
	{
		#region Static variables

		private static FileLogger fileLogger = null;
		private static PersistentStorage persistentStorage;

		private static EthernetBuiltIn ethernet = new EthernetBuiltIn();
		private const string myIP = "192.168.1.220";
		private const string subnetMask = "255.255.255.0";
		private const string gatewayAddress = "192.168.1.1";
		private const string dnsAddresses = "194.246.96.1";
		//	private const byte[] myMAC = new byte[] { 0x00, 0x30, 0xFB, 0x87, 0xD2, 0x3C };

		private const string echoServerUrl = "wss://echo.websocket.org";
		private static WebSocketClient websocketClient;

		private static AutoResetEvent autoEvent;
		private static Timer timer;

		#endregion


		#region Main Method

		public static void Main()
		{
			// init SD card
			if (InitializeSDCard() == false)
			{
				return;
			}

			// init File logger
			/*
			fileLogger = new FileLogger();
			fileLogger.Open(filePath, fileName);
			Logger.Initialize(fileLogger, LogLevel.Info);
			*/

			// init Debug logger
			Logger.Initialize(new DebugLogger(), LogLevel.Info);

			// init network
			if (InitializeNetwork(true) == false)
			{
				return;
			}

			WSOptions wsOptions = new WSOptions();
			wsOptions.MaskingEnabled = true;
			wsOptions.ActivityTimerEnabled = true;

			// init pusher client
			websocketClient = new WebSocketClient("websocket", wsOptions);

			// attach event handlers
			websocketClient.ConnectionChanged += new WSDelegates.ConnectionChangedEventHandler(websocketClient_ConnectionChanged);
			websocketClient.TextMessageReceived += new WSDelegates.TextMessageReceivedEventHandler(websocketClient_TextMessageReceived);
			websocketClient.Error += new WSDelegates.ErrorEventHandler(websocketClient_Error);

			DebugEx.WriteLine("Running...");

			// create autoEvent
			autoEvent = new AutoResetEvent(false);

			// connect to server
			websocketClient.Connect(echoServerUrl);

			// wait to be signalled
			autoEvent.WaitOne();

			// disconnect from server
			websocketClient.Disconnect();

			// wait for disconnect
			DateTime timeoutTime = DateTime.Now.AddSeconds(20);
			while (websocketClient.State != WebSocketState.Disconnected && DateTime.Now < timeoutTime)
				Thread.Sleep(500);

			// destroy the websocket client
			websocketClient.Dispose();

			// close the file logger
			if (fileLogger != null)
			{
				DebugEx.WriteLine("Closing FileLogger...");
				fileLogger.Dispose();
				fileLogger = null;
			}

			// dismount the SD card
			if (persistentStorage != null)
			{
				DebugEx.WriteLine("Dismounting the SD card...");
				persistentStorage.UnmountFileSystem();
				persistentStorage = null;
			}

			Thread.Sleep(2000);
		}

		#endregion


		#region Event Handlers

		private static void websocketClient_ConnectionChanged(WebSocketState websocketState)
		{
			Logger.WriteInfo("Main", string.Concat("Connection changed: ", websocketState.Name()));

			if (websocketState == WebSocketState.Connected)
			{
				Logger.WriteInfo("Main", "Message Sent: test");
				websocketClient.SendText("test");
			}
		}

		private static void websocketClient_TextMessageReceived(string message)
		{
			// show the event
			Logger.WriteInfo("Main", "Message Rcvd: " + message);

			// handle close event
			if (message == "test")
			{
				Logger.WriteInfo("Main", "Message Sent: close");
				websocketClient.SendText("close");
			}
			else if (message == "close")
			{
				timer = new Timer(timerCallback, null, 4000, Timeout.Infinite);
			}
		}

		private static void websocketClient_Error(string message, string stackTrace = null)
		{
			Logger.WriteError("Main", message, stackTrace);
		}

		private static void timerCallback(object stateInfo)
		{
			autoEvent.Set();
		}

		#endregion


		#region Private Methods

		private static bool InitializeSDCard()
		{
			DebugEx.WriteLine("Initializing SD card...");

			if (PersistentStorage.DetectSDCard() == false)
			{
				DebugEx.WriteLine("Failed to detect SD card.");
				return false;
			}

			try
			{
				persistentStorage = new PersistentStorage("SD");
				persistentStorage.MountFileSystem();
				DebugEx.WriteLine("SD card initialized.");
				return true;
			}
			catch
			{
				if (persistentStorage != null)
				{
					persistentStorage.Dispose();
					persistentStorage = null;
				}
				DebugEx.WriteLine("Failed to mount SD card.");
			}

			return false;
		}

		private static bool InitializeNetwork(bool useDHCP)
		{
			DebugEx.WriteLine("Initializing network...");

			Logger.WriteInfo("Main", "Initializing network...");

			try
			{
				if (ethernet.IsActivated)
				{
					ethernet.Close();
					ethernet.Dispose();
					ethernet = new EthernetBuiltIn();
				}

				ethernet.CableConnectivityChanged += new EthernetBuiltIn.CableConnectivityChangedEventHandler(ethernet_CableConnectivityChanged);
				ethernet.NetworkAddressChanged += new NetworkInterfaceExtension.NetworkAddressChangedEventHandler(ethernet_NetworkAddressChanged);

				ethernet.Open();

				if (NetworkInterfaceExtension.AssignedNetworkInterface == null)
				{
					NetworkInterfaceExtension.AssignNetworkingStackTo(ethernet);
				}
				else if (NetworkInterfaceExtension.AssignedNetworkInterface.NetworkInterface.NetworkInterfaceType != NetworkInterfaceType.Ethernet)
				{
					NetworkInterfaceExtension.AssignNetworkingStackTo(ethernet);
				}

				if (ethernet.IsActivated && ethernet.IsOpen && ethernet.IsCableConnected)
				{
					NetworkInterface networkInterface = ethernet.NetworkInterface;

					if (useDHCP)
					{
						Logger.WriteInfo("Main", "Setting dynamic IP address...");
						if (networkInterface.IsDhcpEnabled == true)
						{
							networkInterface.RenewDhcpLease();
						}
						else
						{
							networkInterface.EnableDhcp();
						}

						int waitSecs = 10;
						IPAddress ip = IPAddress.GetDefaultLocalAddress();
						while (ip == IPAddress.Any && waitSecs > 0)
						{
							Thread.Sleep(1000);
							waitSecs--;
							ip = IPAddress.GetDefaultLocalAddress();
						}
					}
					else
					{
						Logger.WriteInfo("Main", "Setting static IP address...");
						networkInterface.EnableStaticIP(myIP, subnetMask, gatewayAddress);

						//	networkInterface.PhysicalAddress = myMAC;
						networkInterface.EnableStaticDns(new[] { dnsAddresses });
					}

					// display results
					if (IPAddress.GetDefaultLocalAddress() != IPAddress.Any)
					{
						Logger.WriteInfo("Main", "Network ready.");
						Logger.WriteInfo("Main", "  IP Address:      " + IPAddress.GetDefaultLocalAddress());
						Logger.WriteInfo("Main", "  IP Address:      " + networkInterface.IPAddress);
						Logger.WriteInfo("Main", "  Subnet Mask:     " + networkInterface.SubnetMask);
						Logger.WriteInfo("Main", "  Default Gateway: " + networkInterface.GatewayAddress);
						Logger.WriteInfo("Main", "  # DNS Servers:   " + networkInterface.DnsAddresses.Length);
						foreach (string dnsAddress in networkInterface.DnsAddresses)
						{
							Logger.WriteInfo("Main", "  DNS Server: " + dnsAddress);
						}
						DebugEx.WriteLine("Network initialized.");
						return true;
					}
					else
					{
						Logger.WriteInfo("Main", "Network failed to initialize.");
						Logger.WriteInfo("Main", "  IP Address: " + IPAddress.GetDefaultLocalAddress());
					}
				}
				else
				{
					Logger.WriteInfo("Main", "No network device found.");
				}
			}
			catch (Exception ex)
			{
				Logger.WriteError("Main", ex.Message, ex.StackTrace);
			}

			DebugEx.WriteLine("Failed to initialize network.");

			return false;
		}

		private static void ethernet_CableConnectivityChanged(object sender, EthernetBuiltIn.CableConnectivityEventArgs e)
		{
		}

		private static void ethernet_NetworkAddressChanged(object sender, EventArgs e)
		{
		}

		#endregion
	}
}
